/**
 * @file task_gnss.c
 * @brief 
 * @author  ()
 * @version 1.0
 * @date 2024-09-13
 * 
 * @copyright Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-09-13 <td>1.0     <td>     <td>内容
 * </table>
 */

#include "cmsis_os2.h"
#include "gnss_power_mgr.h"
#include "qlcommon.h"
#include "freertos/Source/include/mpu_wrappers.h"
#include "gnss_public.h"
#include "gnss_log.h"
#include "bsp_gnss.h"
#include "gnss_request.h"
#include "nmea.h"
#include "mtk_series01.h"
#include "gnss_command.h"
#include "gnss_power_mgr.h"
#include "gnss_info_process.h"

extern void Error_Handler(void);
extern struct fix_info gnss_fix_info;

char gnss_fw_ver[GNSS_FW_VER_LEN+1] = "";
gnss_task_manage_t *gnss_task_manage = NULL;
static const nmea_parse_handler_t mtks01_nmea_tbl[] =
{
    {"PAIR",  mtkS01_pair_parse}
};

#define MTKS01_NMEA_TB_SIZE     (sizeof(mtks01_nmea_tbl) / sizeof(nmea_parse_handler_t))
#define READ_BUF_MAX            256


/* GNSS任务的管理 -------------------------------------------------------------------------------------------------- */
gnss_task_manage_t * get_gnss_manage(void)
{
    return gnss_task_manage;
}

/*
 * @brief  获取 GNSS 固件版本信息
 * @param  version: [O], 固件版本信息.
 * @param  size: 缓存数组大小
 * @retval 0:成功, others:错误码
 */
int qltask_gnss_firmware_version(char *version, size_t size)
{
    int len;

    len = strlen(gnss_fw_ver);
    if (0 >= len) {
        return -ENODATA;
    } else if (NULL == version || size < len+1) {
        return -EINVAL;
    } else {
        taskENTER_CRITICAL();
        strcpy(version, gnss_fw_ver);
        taskEXIT_CRITICAL();
        return 0;
    }
}

/*
 * @brief  获取当前的卫星信息
 * @param  sat: [O], 当前的卫星信息.
 * @retval 0:成功, others:错误码
 */
int qltask_gnss_sat_get(struct sat_info *sat)
{
    vTaskSuspendAll();
    sat->count = 0;
    for (int i=0; i<SV_MAX; i++) {
        if (gnss_task_manage->sat[i].count > 0) {
            memcpy(&(sat->sat[sat->count]), gnss_task_manage->sat[i].sat, gnss_task_manage->sat[i].count*sizeof(struct satellite_t));
            sat->count += gnss_task_manage->sat[i].count;
        }
    }
    xTaskResumeAll();
    return 0;
}

/*
 * @brief GNSS 检查是否通讯异常
 * @param is_get_data 是否读到数据
 * @param data_is_valid 读到的数据是否有效
 */
static void gnss_communication_detect(bool is_get_data, bool data_is_valid)
{
    gnss_task_manage_t *gps_device = get_gnss_manage();
    nmea_protocol_parse_t *nmea_ptr = get_nmea_manage_context();

    if (NULL == nmea_ptr || NULL == gps_device)
        return;

    if (is_get_data){
        /*检测是否一直收到的是乱码*/
        if (data_is_valid){
            /*记录这次收到合法GPS数据的时间*/
            gps_device->comm_err.get_timeout = getms();
            gps_device->comm_err.has_get_msg = true;

            if ((nmea_ptr->recv_nmea_msg & RMC_MASK) &&
                (timeout_ms(gps_device->comm_err.parse_timeout, GNSS_COMM_BUSY_TIME * 1000)) &&
                (nmea_ptr->previous_sec != nmea_ptr->raw_info.utc.tm_sec || nmea_ptr->previous_ms != nmea_ptr->raw_info.utc_ms)){
                gnss_trace(LOG_WARN"GNSS UART receive busy, please try to heighten the baudrate.\r\n");
                /* 长时间持续接收到数据，波特率可能太低，不等接收结束，对数据进行一次处理 */
                if ((nmea_ptr->stream_valid_mask & RMC_MASK) == 0)  /* RMC不定位，一定不定位 */
                    nmea_ptr->raw_info.state = FIX_INVALID;
                else if (nmea_ptr->raw_info.state == FIX_INVALID)   /* RMC定位，最起码是2D定位，同时避免将3D定位改为2D */
                    nmea_ptr->raw_info.state = FIX_OK;

                nmea_ptr->stream_valid_mask = 0;
                nmea_ptr->recv_nmea_msg = 0;
                nmea_ptr->previous_sec = nmea_ptr->raw_info.utc.tm_sec;
                nmea_ptr->previous_ms = nmea_ptr->raw_info.utc_ms;
                /* 定位信息的处理 */
                gnss_info_proc(&nmea_ptr->raw_info);
            }
        }else{
            /* 一定时间未接收到有效信息，改变定位状态为未定位 */
            if(timeout_ms(gps_device->comm_err.get_timeout, GNSS_COMM_ERR_CHG_STATE_TIME * 1000ul)){
                gnss_fix_info.state = FIX_INVALID;
            }
        }
        gps_device->comm_err.lost_timeout = getms();
    }else{
        /*解析数据包，曾收到合法数据，且一阵子未再收到数据，代表这包数据已发送完了*/
        /*单位ms，串口多久未再收到新数据，则认为传输结束*/
        if (gps_device->comm_err.has_get_msg && timeout_ms(gps_device->comm_err.lost_timeout, 20)){
            gps_device->comm_err.has_get_msg = false;
            gps_device->comm_err.parse_timeout = getms();
            if (nmea_ptr->previous_sec == nmea_ptr->raw_info.utc.tm_sec &&
                nmea_ptr->previous_ms == nmea_ptr->raw_info.utc_ms){
                /*此处为了避免从波特率太小一直在收数据，较变为间断性接收时，将最后一包重复处理*/
                return;
            }
            if ((nmea_ptr->recv_nmea_msg & GSA_MASK) == 0 &&
                (nmea_ptr->recv_nmea_msg & GGA_MASK) == 0 &&
                (nmea_ptr->recv_nmea_msg & RMC_MASK) == 0){
                return;/*可能只是收到一些非定位数据，如nmea txt，则return*/
            }
            if ((nmea_ptr->recv_nmea_msg & RMC_MASK) == 0){/*无RMC语句*/
                if(++nmea_ptr->rmc_lost_cnt > 2)/*RMC连续缺失3次，一定不定位*/
                    nmea_ptr->raw_info.state = FIX_INVALID;
            }else{
                nmea_ptr->rmc_lost_cnt = 0;
                if ((nmea_ptr->stream_valid_mask & RMC_MASK) == 0) /*RMC不定位，一定不定位*/
                    nmea_ptr->raw_info.state = FIX_INVALID;
            }
            /* 定位信息的处理 */
            gnss_info_proc(&nmea_ptr->raw_info);

            nmea_ptr->stream_valid_mask = 0;
            nmea_ptr->recv_nmea_msg = 0;
            nmea_ptr->previous_sec = nmea_ptr->raw_info.utc.tm_sec;
            nmea_ptr->previous_ms = nmea_ptr->raw_info.utc_ms;
        }

        if(timeout_ms(gps_device->comm_err.get_timeout, GNSS_COMM_ERR_CHG_STATE_TIME * 1000ul)){
            gnss_fix_info.state = FIX_INVALID;
        }

        if (timeout_ms(gps_device->comm_err.lost_timeout, GNSS_COMM_LOSE_TIME*1000ul)) {
            gps_device->comm_err.lost_timeout = getms();
            gnss_trace(LOG_ERROR"Communicate error event occurred!\r\n");
        }
    }
}

/*
 * @brief  GNSS 数据接收处理
 */
static void qltask_gnss_read_process(void)
{
    gnss_task_manage_t *gps_device = get_gnss_manage();
    int32_t rlen    = 0;
    uint8_t *rbuf   = NULL;
    int8_t  cnt     = 0;
    int32_t ret     = -1;

    rbuf = qlmalloc(READ_BUF_MAX);
    if (rbuf == NULL)
        return;

    do {
        rlen = bsp_gnss_read(rbuf, READ_BUF_MAX - 1);
        if (rlen > 0){
            ret = nmea_protocol_parse(rbuf, rlen, (void*)mtks01_nmea_tbl, MTKS01_NMEA_TB_SIZE);
            if (ret==0)
                gnss_communication_detect(true, true);
            else
                gnss_communication_detect(true, false);
        }else{
            gnss_communication_detect(false,false);
        }
    } while((rlen>0)&&(cnt++<=5));

    qlfree(rbuf);
}

/*
 * @brief  GNSS 业务逻辑处理
 */
static void qltask_gnss_application_process(void)
{
    /* 指令发送处理 */
    gnss_cmd_process(NULL);
    
    /* 数据接收处理 */
    qltask_gnss_read_process();

    /* 处理请求 */
    gnssmgr_request_handler();

    /* 定时更新时间 */
    reflesh_rtc_time_from_gps();
}

/**
 * @brief GNSS 全局管理参数初始化
 */
static void qltask_gnss_manage_param_init(void)
{
    unsigned int time = 0;

    if (NULL == gnss_task_manage) {
        gnss_task_manage = qlmalloc(sizeof(gnss_task_manage_t));
    }

    if (NULL == gnss_task_manage) {
        Error_Handler();
        return;
    }

    memset(gnss_task_manage, 0, sizeof(gnss_task_manage_t));
    memset(&gnss_fix_info, 0, sizeof(struct fix_info));

    time = getms();
    /* 初始化参数 */
    gnss_task_manage->comm_err.lost_timeout     = time;
    gnss_task_manage->comm_err.get_timeout      = time;
    gnss_task_manage->comm_err.parse_timeout    = time;
    gnss_task_manage->comm_err.fix_timeout      = time;
    gnss_task_manage->gnss_info.recv_uptime     = time;                         /* 通信超时 */
}

/*
 * @brief  GNSS 芯片初始化配置
 */
static void qltask_gnss_device_config_init(void)
{
    gnss_cmd_register(GNSS_CMD_SYSTEM_ON, NULL);
    gnss_cmd_register(GNSS_CMD_BAUDRADE, NULL);
    gnss_cmd_register(GNSS_CMD_PRM_SAVE, NULL);
    gnss_cmd_register(GNSS_CMD_GNSS_MODE_SET, NULL);
    gnss_cmd_register(GNSS_CMD_MSG_SET, NULL);
    gnss_cmd_register(GNSS_CMD_PRM_SAVE, NULL);
    gnss_cmd_register(GNSS_CMD_CONFIG_DONE, NULL);
    gnss_trace(LOG_INFO"Change status to INITIALIZING.\r\n");
}

/*
 * @brief  GNSS 业务逻辑初始化
 */
static void qltask_gnss_application_init(void)
{
    gnssmgr_power_manage_init();
}

/**
 * @brief GNSS 任务初始化
 */
static void task_gnss_init(void)
{
    /* 初始化硬件 */
    gnss_hw_init();

    /* 注册芯片配置指令 */
    qltask_gnss_device_config_init();

    /* 业务逻辑初始化 */
    qltask_gnss_application_init();
}

enum {
    GNSS_STAGE_IDLE,
    GNSS_STAGE_INIT,
    GNSS_STAGE_WORK,
};

/**
 * @brief GNSS 任务主循环
 */
static void task_gnss_entry(void)
{
    static unsigned char stage = 0;
    bool status = gnss_power_status_get();

    /* 电源管理 */
    gnssmgr_power_manage_process();

    switch (stage) {
    case GNSS_STAGE_IDLE:
        if (status)
            stage = GNSS_STAGE_INIT;
        break;

    case GNSS_STAGE_INIT:
        /* 每次上电都重新初始化参数 */
        qltask_gnss_manage_param_init();
        stage = GNSS_STAGE_WORK;
        break;

    case 2:
        if (!status) {
            stage = GNSS_STAGE_IDLE;
            break;
        }
        /* 处理业务逻辑 */
        qltask_gnss_application_process();
        break;

    default:
        stage = GNSS_STAGE_IDLE;
        break;
    }
}

/**
 * @brief 低功耗任务主循环
 * @param param 参数(低功耗任务对象)
 */
static void task_gnss_thread(void *param)
{
    task_gnss_init();

    while (1) {
        task_gnss_entry();
        osDelay(100); // TODO: need covert tick to ms
    }
}

/**
 * @brief 低功耗任务启动函数
 */
int32_t qltask_gnss_start(void)
{
    if (pdPASS != xTaskCreate(task_gnss_thread, "gnss", TASKSTACK_GNSS, NULL, TASKPRI_GNSS, NULL))
    {
        Error_Handler();
    }

    return 0;
}
